/*
 * @Author: lin_zizhuo 2297304135@qq.com
 * @Date: 2023-12-10 19:14:20
 * @LastEditors: lin_zizhuo 2297304135@qq.com
 * @LastEditTime: 2023-12-10 20:04:31
 * @FilePath: /cp_model/blockQueue.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#pragma once
#include <queue>
#include <pthread.h>
#include <unistd.h>

using namespace std;

// 谁来唤醒呢? 生产者最清楚当前有余量，生产者唤醒消费者
// 同理

// 这个阻塞队列是把整体当作资源来使用

template<typename T>
class block_queue
{
    //默认最大的队列元素只有5个
    static const int default_num = 5;
public:
    void push(const T& t)
    {
        // 由于c++的stl容器不是线程安全的，所以多线程要修改其中的信息需要加锁
        pthread_mutex_lock(&_mutex);
        // 如果当前队列容量已经达到最大值，就应该让当前线程进入等待队列
        // 需要注意的是，这里为了防止误唤醒，需要while循环
        while(_q.size() == _maxq)
            // 为什么进入等待队列的接口要传入锁资源?
            // 因为你要让一个进程等待，需要自己用进行判断
            // 而判断语句也是线程不安全的，所以必须要先加锁！！
            // 进入等待队列后，该函数会自动释放锁资源
            pthread_cond_wait(&_p_cond, &_mutex);
        _q.push(t);
        if(_q.size() > _top_line) 
            pthread_cond_signal(&_c_cond);

        pthread_mutex_unlock(&_mutex);
    }

    T pop()
    {
        // 和push相同
        pthread_mutex_lock(&_mutex);
        // 同理
        while(_q.empty())
            pthread_cond_wait(&_c_cond, &_mutex);
        const T& t = _q.front();
        _q.pop();
        if(_q.size() < _bottom_line) 
            pthread_cond_signal(&_p_cond);
        pthread_mutex_unlock(&_mutex);
        return t;
    }


    block_queue(int max_num = default_num)
        : _maxq(max_num)
        , _bottom_line(max_num / 3)
        , _top_line(max_num * 2 / 3)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
        pthread_cond_init(&_p_cond, nullptr);
    }
    ~block_queue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_c_cond);
        pthread_cond_destroy(&_p_cond);
    }
private:
    queue<T> _q;
    int _maxq;
    pthread_mutex_t _mutex;
    pthread_cond_t _c_cond;
    pthread_cond_t _p_cond;
    int _bottom_line;
    int _top_line; 
};